/*
 * Direct3D.cpp
 *
 *  Created on: May 28, 2011
 *      Author: Ernest Mashele <mashern@gmail.com>
 */

#include "Direct3D.h"

Direct3D::Direct3D() :
	pDevice(0), pSwapChain(0), pRenderTargetView(0), pDepthStencil(0),
			pDepthStencilView(0), pSolidState(0)
{
}

Direct3D::~Direct3D()
{
	Shutdown();
}

bool Direct3D::Initialize(HWND hWnd)
{
	HRESULT hr = S_OK;

	RECT rc;
	GetClientRect(hWnd, &rc);
	UINT width = rc.right - rc.left;
	UINT height = rc.bottom - rc.top;

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory(&sd, sizeof(sd));
	sd.BufferCount = 1;
	sd.BufferDesc.Width = width;
	sd.BufferDesc.Height = height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = hWnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

	// Create the swap chain and the Direct3D device.
	hr = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL,
			NULL, D3D10_SDK_VERSION, &sd, &pSwapChain, &pDevice);
	if (FAILED(hr))
	{
		return Error(L"failed operation: D3D10CreateDeviceAndSwapChain.");
	}

	ID3D10Texture2D* pBuffer;
	hr
			= pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D),
					(LPVOID*) &pBuffer);
	if (FAILED(hr))
	{
		return Error(L"failed operation: GetBuffer (back buffer)");
	}

	// Create the render target view with the back buffer pointer.
	hr = pDevice->CreateRenderTargetView(pBuffer, NULL, &pRenderTargetView);
	if (FAILED(hr))
	{
		return Error(L"failed operation: CreateRenderTargetView");
	}

	SafeRelease(pBuffer);

	// Initialize the description of the depth buffer.
	D3D10_TEXTURE2D_DESC depthBufferDesc;
	ZeroMemory(&depthBufferDesc, sizeof(depthBufferDesc));

	// Set up the description of the depth buffer.
	depthBufferDesc.Width = width;
	depthBufferDesc.Height = height;
	depthBufferDesc.MipLevels = 1;
	depthBufferDesc.ArraySize = 1;
	depthBufferDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthBufferDesc.SampleDesc.Count = 1;
	depthBufferDesc.SampleDesc.Quality = 0;
	depthBufferDesc.Usage = D3D10_USAGE_DEFAULT;
	depthBufferDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
	depthBufferDesc.CPUAccessFlags = 0;
	depthBufferDesc.MiscFlags = 0;

	// Create the texture for the depth buffer using the filled out description.
	hr = pDevice->CreateTexture2D(&depthBufferDesc, NULL, &pDepthStencil);
	if (FAILED(hr))
	{
		return Error(L"failed operation: CreateTexture2D (DepthStencil buffer)");
	}

	D3D10_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
	ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));
	depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencilViewDesc.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
	depthStencilViewDesc.Texture2D.MipSlice = 0;

	// Create the depth stencil view.
	hr = pDevice->CreateDepthStencilView(pDepthStencil, &depthStencilViewDesc,
			&pDepthStencilView);
	if (FAILED(hr))
	{
		return Error(L"failed operation: CreateDepthStencilView");
	}

	pDevice->OMSetRenderTargets(1, &pRenderTargetView, pDepthStencilView);

	D3D10_RASTERIZER_DESC rasterDesc;
	rasterDesc.AntialiasedLineEnable = false;
	rasterDesc.CullMode = D3D10_CULL_BACK;
	rasterDesc.DepthBias = 0;
	rasterDesc.DepthBiasClamp = 0.0f;
	rasterDesc.DepthClipEnable = true;
	rasterDesc.FillMode = D3D10_FILL_SOLID;
	rasterDesc.FrontCounterClockwise = false;
	rasterDesc.MultisampleEnable = false;
	rasterDesc.ScissorEnable = false;
	rasterDesc.SlopeScaledDepthBias = 0.0f;

	hr = pDevice->CreateRasterizerState(&rasterDesc, &pSolidState);
	if (FAILED(hr))
	{
		return Error(L"failed operation: CreateRasterizerState");
	}

	pDevice->RSSetState(pSolidState);

	D3D10_VIEWPORT viewport;
	viewport.Width = width;
	viewport.Height = height;
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;
	viewport.TopLeftX = 0;
	viewport.TopLeftY = 0;
	pDevice->RSSetViewports(1, &viewport);

	float fov = (float) D3DX_PI / 4.0f;
	float aspect = (float) width / (float) height;

	D3DXMatrixPerspectiveFovLH(&Projection, fov, aspect, 0.1f, 1000.0f);

	D3DXMatrixIdentity(&World);

	D3DXMatrixOrthoLH(&Orthographic, (float) width, (float) height, 0.1f,
			1000.0f);

	return true;
}

void Direct3D::Shutdown()
{

	SafeRelease(pSolidState);
	SafeRelease(pDepthStencilView);
	SafeRelease(pDepthStencil);
	SafeRelease(pRenderTargetView);
	SafeRelease(pSwapChain);
	SafeRelease(pDevice);
}

void Direct3D::BeginScene()
{
	// Clear the back buffer.
	pDevice->ClearRenderTargetView(pRenderTargetView, kBackground);

	// Clear the depth buffer.
	pDevice->ClearDepthStencilView(pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f,
			0);
}

void Direct3D::EndScene()
{
	pSwapChain->Present(0, 0);
}

ID3D10Device* Direct3D::GetDevice()
{
	return pDevice;
}

void Direct3D::GetProjectionMatrix(D3DXMATRIX& Projection)
{
	Projection = this->Projection;
}

void Direct3D::GetWorldMatrix(D3DXMATRIX& World)
{
	World = this->World;
}

void Direct3D::GetOrthoMatrix(D3DXMATRIX& Orthographic)
{
	Orthographic = this->Orthographic;
}
